#!/bin/bash

#==============================================================================
# 批量文件重命名器 (Batch File Renamer)
#==============================================================================
# 
# 功能描述:
#   提供多种模式的批量文件重命名功能
#   支持序号重命名、大小写转换、字符替换、扩展名修改等
#
# 作者: Pedro.Pei
# 版本: 1.0
# 更新日期: 2024
#
# 支持的重命名模式:
#   🔢 序号模式: 按序号重命名文件 (file_001.txt, file_002.txt...)
#   🔤 大小写模式: 转换文件名大小写
#   🔄 替换模式: 替换文件名中的指定字符串
#   📝 扩展名模式: 批量修改文件扩展名
#   📅 日期模式: 添加日期前缀或后缀
#   🎯 自定义模式: 使用自定义模板重命名
#
# 特性:
#   ✅ 支持预览模式，安全确认后再执行
#   ✅ 自动处理文件名冲突
#   ✅ 支持递归处理子目录
#   ✅ 详细的操作日志和统计信息
#   ✅ 支持撤销操作（保存重命名历史）
#   ✅ 彩色输出和进度显示
#
# 使用方法:
#   ./batch_rename.sh <目录路径> [选项]
#
# 选项:
#   -m, --mode <模式>        重命名模式 (number|case|replace|extension|date|custom)
#   -p, --pattern <模式>     重命名模式的具体参数
#   -r, --recursive         递归处理子目录
#   -f, --filter <过滤器>    文件过滤器 (如: *.jpg,*.png)
#   --preview              仅预览，不执行重命名
#   --undo                 撤销上次重命名操作
#   -v, --verbose          显示详细信息
#   -h, --help             显示帮助信息
#
# 使用示例:
#   # 序号重命名 (photo_001.jpg, photo_002.jpg...)
#   ./batch_rename.sh ~/Pictures -m number -p "photo"
#   
#   # 转换为小写
#   ./batch_rename.sh ~/Documents -m case -p "lower"
#   
#   # 替换字符串
#   ./batch_rename.sh ~/Downloads -m replace -p "old_name,new_name"
#   
#   # 修改扩展名
#   ./batch_rename.sh ~/Videos -m extension -p "mp4" -f "*.avi"
#   
#   # 添加日期前缀
#   ./batch_rename.sh ~/Photos -m date -p "prefix"
#   
#   # 预览模式
#   ./batch_rename.sh ~/Test --preview -m number -p "test"
#   
#   # 撤销操作
#   ./batch_rename.sh ~/Test --undo
#
#==============================================================================

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color

# 全局变量
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
TARGET_DIR=""
MODE=""
PATTERN=""
RECURSIVE=false
FILTER="*"
PREVIEW=false
UNDO=false
VERBOSE=false
LOG_FILE=""
UNDO_FILE=""

# 显示帮助信息
show_help() {
    echo -e "${CYAN}🔄 批量文件重命名器${NC}"
    echo ""
    echo -e "${WHITE}使用方法:${NC}"
    echo "  $0 <目录路径> [选项]"
    echo ""
    echo -e "${WHITE}重命名模式:${NC}"
    echo -e "  ${GREEN}number${NC}     序号重命名 (file_001.txt, file_002.txt...)"
    echo -e "  ${GREEN}case${NC}       大小写转换 (upper/lower/title)"
    echo -e "  ${GREEN}replace${NC}    字符串替换 (old_string,new_string)"
    echo -e "  ${GREEN}extension${NC}  扩展名修改 (new_extension)"
    echo -e "  ${GREEN}date${NC}       日期前缀/后缀 (prefix/suffix)"
    echo -e "  ${GREEN}custom${NC}     自定义模板 (template_string)"
    echo ""
    echo -e "${WHITE}选项:${NC}"
    echo -e "  ${GREEN}-m, --mode${NC}        重命名模式"
    echo -e "  ${GREEN}-p, --pattern${NC}     模式参数"
    echo -e "  ${GREEN}-r, --recursive${NC}   递归处理子目录"
    echo -e "  ${GREEN}-f, --filter${NC}      文件过滤器 (如: *.jpg,*.png)"
    echo -e "  ${GREEN}--preview${NC}         仅预览，不执行"
    echo -e "  ${GREEN}--undo${NC}            撤销上次操作"
    echo -e "  ${GREEN}-v, --verbose${NC}     显示详细信息"
    echo -e "  ${GREEN}-h, --help${NC}        显示此帮助"
    echo ""
    echo -e "${WHITE}示例:${NC}"
    echo "  $0 ~/Pictures -m number -p \"photo\""
    echo "  $0 ~/Documents -m case -p \"lower\""
    echo "  $0 ~/Downloads -m replace -p \"old,new\""
    echo "  $0 ~/Videos -m extension -p \"mp4\" -f \"*.avi\""
    echo "  $0 ~/Photos -m date -p \"prefix\""
    echo "  $0 ~/Files -m custom -p \"{date}_{name}_{index}.{ext}\""
    echo ""
    echo -e "${WHITE}自定义模板变量:${NC}"
    echo -e "  ${GREEN}{name}${NC}     原文件名（不含扩展名）"
    echo -e "  ${GREEN}{ext}${NC}      文件扩展名"
    echo -e "  ${GREEN}{index}${NC}    序号（3位数字）"
    echo -e "  ${GREEN}{date}${NC}     当前日期（YYYYMMDD）"
}

# 解析命令行参数
parse_arguments() {
    if [[ $# -eq 0 ]]; then
        show_help
        exit 1
    fi
    
    # 首先检查是否有帮助参数
    for arg in "$@"; do
        if [[ "$arg" == "-h" || "$arg" == "--help" ]]; then
            show_help
            exit 0
        fi
    done
    
    TARGET_DIR="$1"
    shift
    
    while [[ $# -gt 0 ]]; do
        case $1 in
            -m|--mode)
                MODE="$2"
                shift 2
                ;;
            -p|--pattern)
                PATTERN="$2"
                shift 2
                ;;
            -r|--recursive)
                RECURSIVE=true
                shift
                ;;
            -f|--filter)
                FILTER="$2"
                shift 2
                ;;
            --preview)
                PREVIEW=true
                shift
                ;;
            --undo)
                UNDO=true
                shift
                ;;
            -v|--verbose)
                VERBOSE=true
                shift
                ;;
            -h|--help)
                show_help
                exit 0
                ;;
            *)
                echo -e "${RED}错误: 未知选项 $1${NC}"
                show_help
                exit 1
                ;;
        esac
    done
}

# 验证参数
validate_arguments() {
    # 检查目录是否存在
    if [[ ! -d "$TARGET_DIR" ]]; then
        echo -e "${RED}错误: 目录不存在: $TARGET_DIR${NC}"
        exit 1
    fi
    
    # 如果不是撤销操作，检查模式参数
    if [[ "$UNDO" != true ]]; then
        if [[ -z "$MODE" ]]; then
            echo -e "${RED}错误: 请指定重命名模式 (-m)${NC}"
            show_help
            exit 1
        fi
        
        case "$MODE" in
            number|case|replace|extension|date|custom)
                ;;
            *)
                echo -e "${RED}错误: 无效的重命名模式: $MODE${NC}"
                show_help
                exit 1
                ;;
        esac
        
        if [[ -z "$PATTERN" && "$MODE" != "number" ]]; then
            echo -e "${RED}错误: 请指定模式参数 (-p)${NC}"
            show_help
            exit 1
        fi
    fi
    
    # 设置日志文件路径
    LOG_FILE="$TARGET_DIR/.batch_rename_log_$(date +%Y%m%d_%H%M%S).txt"
    UNDO_FILE="$TARGET_DIR/.batch_rename_undo.txt"
}

# 记录日志
log_message() {
    local message="$1"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] $message" >> "$LOG_FILE"
    
    if [[ "$VERBOSE" == true ]]; then
        echo -e "${BLUE}[LOG]${NC} $message"
    fi
}

# 获取文件列表
get_file_list() {
    local files=()
    
    if [[ "$RECURSIVE" == true ]]; then
        while IFS= read -r -d '' file; do
            files+=("$file")
        done < <(find "$TARGET_DIR" -type f -name "$FILTER" -print0)
    else
        # 防止通配符展开，使用引号保护
        local filter_patterns="$FILTER"
        IFS=',' read -ra patterns <<< "$filter_patterns"
        for pattern in "${patterns[@]}"; do
            # 去除前后空格
            pattern=$(echo "$pattern" | xargs)
            while IFS= read -r -d '' file; do
                files+=("$file")
            done < <(find "$TARGET_DIR" -maxdepth 1 -type f -name "$pattern" -print0)
        done
    fi
    
    printf '%s\n' "${files[@]}"
}

# 生成新文件名
generate_new_name() {
    local filepath="$1"
    local index="$2"
    local dirname=$(dirname "$filepath")
    local basename=$(basename "$filepath")
    local filename="${basename%.*}"
    local extension="${basename##*.}"
    
    # 如果文件没有扩展名，extension就是整个文件名
    if [[ "$filename" == "$extension" ]]; then
        extension=""
    fi
    
    local new_name=""
    
    case "$MODE" in
        number)
            local prefix="${PATTERN:-file}"
            local padded_index=$(printf "%03d" "$index")
            if [[ -n "$extension" ]]; then
                new_name="${prefix}_${padded_index}.${extension}"
            else
                new_name="${prefix}_${padded_index}"
            fi
            ;;
        case)
            case "$PATTERN" in
                upper)
                    filename=$(echo "$filename" | tr '[:lower:]' '[:upper:]')
                    ;;
                lower)
                    filename=$(echo "$filename" | tr '[:upper:]' '[:lower:]')
                    ;;
                title)
                    filename=$(echo "$filename" | sed 's/\b\w/\U&/g')
                    ;;
                *)
                    echo -e "${RED}错误: 无效的大小写模式: $PATTERN${NC}"
                    return 1
                    ;;
            esac
            if [[ -n "$extension" ]]; then
                new_name="${filename}.${extension}"
            else
                new_name="$filename"
            fi
            ;;
        replace)
            local old_str="${PATTERN%,*}"
            local new_str="${PATTERN#*,}"
            if [[ "$old_str" == "$PATTERN" ]]; then
                echo -e "${RED}错误: 替换模式格式错误，应为 'old,new'${NC}"
                return 1
            fi
            filename="${filename//$old_str/$new_str}"
            if [[ -n "$extension" ]]; then
                new_name="${filename}.${extension}"
            else
                new_name="$filename"
            fi
            ;;
        extension)
            if [[ -n "$extension" ]]; then
                new_name="${filename}.${PATTERN}"
            else
                new_name="${basename}.${PATTERN}"
            fi
            ;;
        date)
            local date_str=$(date '+%Y%m%d')
            case "$PATTERN" in
                prefix)
                    if [[ -n "$extension" ]]; then
                        new_name="${date_str}_${filename}.${extension}"
                    else
                        new_name="${date_str}_${basename}"
                    fi
                    ;;
                suffix)
                    if [[ -n "$extension" ]]; then
                        new_name="${filename}_${date_str}.${extension}"
                    else
                        new_name="${basename}_${date_str}"
                    fi
                    ;;
                *)
                    echo -e "${RED}错误: 无效的日期模式: $PATTERN${NC}"
                    return 1
                    ;;
            esac
            ;;
        custom)
            # 自定义模板支持变量替换
            # {name} = 原文件名, {ext} = 扩展名, {index} = 序号, {date} = 日期
            new_name="$PATTERN"
            new_name="${new_name//\{name\}/$filename}"
            new_name="${new_name//\{ext\}/$extension}"
            new_name="${new_name//\{index\}/$(printf "%03d" "$index")}"
            new_name="${new_name//\{date\}/$(date '+%Y%m%d')}"
            ;;
    esac
    
    echo "$new_name"
}

# 处理文件名冲突
handle_conflict() {
    local target_path="$1"
    local dirname=$(dirname "$target_path")
    local basename=$(basename "$target_path")
    local filename="${basename%.*}"
    local extension="${basename##*.}"
    
    if [[ "$filename" == "$extension" ]]; then
        extension=""
    fi
    
    local counter=1
    local new_path="$target_path"
    
    while [[ -e "$new_path" ]]; do
        if [[ -n "$extension" ]]; then
            new_path="$dirname/${filename}_${counter}.${extension}"
        else
            new_path="$dirname/${basename}_${counter}"
        fi
        ((counter++))
    done
    
    echo "$new_path"
}

# 执行重命名
perform_rename() {
    local files=()
    while IFS= read -r line; do
        files+=("$line")
    done < <(get_file_list)
    
    if [[ ${#files[@]} -eq 0 ]]; then
        echo -e "${YELLOW}没有找到匹配的文件${NC}"
        return 0
    fi
    
    echo -e "${CYAN}找到 ${#files[@]} 个文件${NC}"
    echo -e "${GRAY}正在准备重命名操作...${NC}"
    echo ""
    
    # 准备重命名操作
    local operations=()
    local index=1
    
    for file in "${files[@]}"; do
        local dirname=$(dirname "$file")
        local new_name=$(generate_new_name "$file" "$index")
        
        if [[ $? -ne 0 ]]; then
            continue
        fi
        
        local new_path="$dirname/$new_name"
        
        # 检查是否需要重命名
        if [[ "$file" == "$new_path" ]]; then
            if [[ "$VERBOSE" == true ]]; then
                echo -e "${YELLOW}跳过: $(basename "$file") (无需更改)${NC}"
            fi
            continue
        fi
        
        # 处理文件名冲突
        if [[ -e "$new_path" ]]; then
            new_path=$(handle_conflict "$new_path")
        fi
        
        operations+=("$file|$new_path")
        ((index++))
    done
    
    if [[ ${#operations[@]} -eq 0 ]]; then
        echo -e "${YELLOW}没有需要重命名的文件${NC}"
        return 0
    fi
    
    # 显示预览
    echo -e "${WHITE}重命名预览:${NC}"
    echo ""
    for op in "${operations[@]}"; do
        local old_file="${op%|*}"
        local new_file="${op#*|}"
        echo -e "  ${GREEN}$(basename "$old_file")${NC} → ${CYAN}$(basename "$new_file")${NC}"
    done
    echo ""
    
    # 如果是预览模式，直接返回
    if [[ "$PREVIEW" == true ]]; then
        echo -e "${YELLOW}预览模式，未执行实际重命名${NC}"
        return 0
    fi
    
    # 确认执行
    echo -e -n "${WHITE}确认执行重命名操作? (y/N): ${NC}"
    read -r confirm
    
    if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
        echo -e "${YELLOW}操作已取消${NC}"
        return 0
    fi
    
    # 执行重命名
    echo ""
    echo -e "${GREEN}开始执行重命名...${NC}"
    
    # 清空撤销文件
    > "$UNDO_FILE"
    
    local success_count=0
    local error_count=0
    
    for op in "${operations[@]}"; do
        local old_file="${op%|*}"
        local new_file="${op#*|}"
        
        if mv "$old_file" "$new_file" 2>/dev/null; then
            echo -e "  ✅ ${GREEN}$(basename "$old_file")${NC} → ${CYAN}$(basename "$new_file")${NC}"
            echo "$new_file|$old_file" >> "$UNDO_FILE"
            log_message "重命名成功: $old_file → $new_file"
            ((success_count++))
        else
            echo -e "  ❌ ${RED}失败: $(basename "$old_file")${NC}"
            log_message "重命名失败: $old_file → $new_file"
            ((error_count++))
        fi
    done
    
    echo ""
    echo -e "${GREEN}重命名完成!${NC}"
    echo -e "  成功: ${GREEN}$success_count${NC} 个文件"
    if [[ $error_count -gt 0 ]]; then
        echo -e "  失败: ${RED}$error_count${NC} 个文件"
    fi
    echo -e "  日志: ${BLUE}$LOG_FILE${NC}"
    echo -e "  撤销: ${YELLOW}$0 \"$TARGET_DIR\" --undo${NC}"
}

# 撤销重命名
perform_undo() {
    if [[ ! -f "$UNDO_FILE" ]]; then
        echo -e "${YELLOW}没有找到撤销信息${NC}"
        return 1
    fi
    
    local operations=()
    while IFS='|' read -r new_file old_file; do
        if [[ -n "$new_file" && -n "$old_file" ]]; then
            operations+=("$new_file|$old_file")
        fi
    done < "$UNDO_FILE"
    
    if [[ ${#operations[@]} -eq 0 ]]; then
        echo -e "${YELLOW}没有可撤销的操作${NC}"
        return 1
    fi
    
    echo -e "${CYAN}撤销预览:${NC}"
    echo ""
    for op in "${operations[@]}"; do
        local current_file="${op%|*}"
        local original_file="${op#*|}"
        if [[ -e "$current_file" ]]; then
            echo -e "  ${GREEN}$(basename "$current_file")${NC} → ${CYAN}$(basename "$original_file")${NC}"
        else
            echo -e "  ${RED}文件不存在: $(basename "$current_file")${NC}"
        fi
    done
    echo ""
    
    echo -e -n "${WHITE}确认撤销操作? (y/N): ${NC}"
    read -r confirm
    
    if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
        echo -e "${YELLOW}撤销已取消${NC}"
        return 0
    fi
    
    echo ""
    echo -e "${GREEN}开始撤销...${NC}"
    
    local success_count=0
    local error_count=0
    
    for op in "${operations[@]}"; do
        local current_file="${op%|*}"
        local original_file="${op#*|}"
        
        if [[ -e "$current_file" ]]; then
            if mv "$current_file" "$original_file" 2>/dev/null; then
                echo -e "  ✅ ${GREEN}$(basename "$current_file")${NC} → ${CYAN}$(basename "$original_file")${NC}"
                log_message "撤销成功: $current_file → $original_file"
                ((success_count++))
            else
                echo -e "  ❌ ${RED}失败: $(basename "$current_file")${NC}"
                log_message "撤销失败: $current_file → $original_file"
                ((error_count++))
            fi
        else
            echo -e "  ⚠️  ${YELLOW}文件不存在: $(basename "$current_file")${NC}"
            ((error_count++))
        fi
    done
    
    echo ""
    echo -e "${GREEN}撤销完成!${NC}"
    echo -e "  成功: ${GREEN}$success_count${NC} 个文件"
    if [[ $error_count -gt 0 ]]; then
        echo -e "  失败: ${RED}$error_count${NC} 个文件"
    fi
    
    # 清空撤销文件
    > "$UNDO_FILE"
}

# 主函数
main() {
    local start_time=$(date +%s)
    parse_arguments "$@"
    
    echo -e "${CYAN}🔄 批量文件重命名器${NC}"
    if [[ "$VERBOSE" == true ]]; then
        echo -e "${YELLOW}开始时间: $(date)${NC}"
    fi
    echo ""
    
    validate_arguments
    
    if [[ "$UNDO" == true ]]; then
        perform_undo
    else
        perform_rename
    fi
    
    # 显示总耗时
    local end_time=$(date +%s)
    local duration=$((end_time - start_time))
    if [[ "$VERBOSE" == true ]] || [[ "$duration" -gt 3 ]]; then
        echo -e "${GREEN}✅ 操作完成，耗时: ${duration}秒${NC}"
    fi
}

# 运行主函数
main "$@"